//
//  TileList.m
//  MapEdit
//
//  Created by Steven Canfield on 16/06/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import "TileList.h"

void SwapTile( tile_t * tile ) {
	int index;
	for( index = 0; index < 16; index++ ) {
		(*tile).micro_tiles[index] = NSSwapLittleShortToHost((*tile).micro_tiles[index]);
	}
}

#define TEXTURE_WIDTH 1024
#define TEXTURE_HEIGHT 1024

@implementation TileList

static NSString * ARRAY_PLACEHOLDER = @"ArrayPlaceholderString";

- (id)initWithData:(NSData *)data pieceList:(TilePieceList *)pieces palette:(ColorPalette *)palette {
	if( (self = [super init]) ) {
		int dataIndex = 0;
		int tileIndex = 0;
		_pieceList = [pieces retain];
		_palette = [palette retain];
		_tilesBitmap = [[NSMutableArray alloc] init];
		int bitmapIndex;
		for( bitmapIndex = 0; bitmapIndex < [data length] / 32; bitmapIndex++ ) {
			[_tilesBitmap addObject:@"ArrayPlaceholderString"];
		}
		
		_tiles = malloc( sizeof( tile_t * ) * [data length] / 32); 
		while( dataIndex < [data length] ) {
			tile_t * tile = malloc( sizeof( tile_t ) );
			[data getBytes:tile range:NSMakeRange( dataIndex, sizeof( tile_t ))];
			dataIndex += sizeof( tile_t );
			SwapTile( tile );
			_tiles[ tileIndex ] = tile;
			tileIndex++;
		}
		
		_tileCount = tileIndex;
		NSLog(@"Loaded %i Tiles", _tileCount );
		
		[self createTexture];
	}
	return self;
}

- (TilePieceList *)pieceList
{
	return _pieceList;
}

- (tile_t)tileAtIndex:(int)index {
	if( index < 0 || index >= _tileCount )	{
		DebugLog(@"Tile Index (%i) Out of Range (%i)", index, _tileCount );
	}
	
	return *_tiles[ index ];
}



- (void)createTexture {
	/* Enable rectangle texturing */
	glEnable( GL_TEXTURE_RECTANGLE_ARB );
		
	/* Now we create a proxy texture to test if this computer supports textures this big */
	GLuint proxyTextureID	= 0;
	GLint  width			= 0;
	glGenTextures( 1 , &proxyTextureID );
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB , proxyTextureID );
	glTexImage2D( GL_PROXY_TEXTURE_RECTANGLE_ARB , 0 , GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0 , GL_RGBA , GL_UNSIGNED_BYTE , NULL );
	glGetTexLevelParameteriv( GL_PROXY_TEXTURE_RECTANGLE_ARB , 0 , GL_TEXTURE_WIDTH , &width );
	if( width == 0 ) {
		/* We couldn't allocate a texture of this size. Bail! */
		NSLog(@"Couldn't allocate textures for TileList.");
		exit(-1);
	}
	/* Cleanup proxy texture */
	glDeleteTextures( 1, &proxyTextureID );
	
	 /* Count how many 1024x1024 tiles we need and allocate storage for them */
	_textureCount = ceil( (float)_tileCount / 1024.0 );
	_textureID = malloc( sizeof( GLuint ) * _textureCount );
	glGenTextures( _textureCount , _textureID );

	int i = 0;
	for( i = 0; i < _textureCount; i++ ) {
		printf("TexID=%i\n", _textureID[i] ); 
	}
	
	/* Create storage for tileInfo */
	_tileInfo = malloc( sizeof( tileInfo ) * _tileCount );
	
	int textureIdx	= 0;	
	for( textureIdx = 0; textureIdx < _textureCount; textureIdx++ ) {
		/* Bind the texture and setup texture options */
		glBindTexture( GL_TEXTURE_RECTANGLE_ARB , _textureID[textureIdx]);
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S , GL_CLAMP );
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T , GL_CLAMP );
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER , GL_NEAREST );
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER , GL_NEAREST );
		glTexEnvf( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE );
		
		/* Allocate storage for texture array */
		u_int8_t * pixels = malloc( sizeof( u_int8_t ) * TEXTURE_WIDTH * TEXTURE_HEIGHT * 3);
		
		int X = 0;
		int Y = 0;
		
		int tileIdx	= 0;
		for( tileIdx = 1024 * (textureIdx); tileIdx < 1024 * (textureIdx+1) && tileIdx < _tileCount; tileIdx++ ) {
			tile_t tile = [self tileAtIndex: tileIdx ];
			
			_tileInfo[ tileIdx ].texID	= _textureID[textureIdx];
			_tileInfo[ tileIdx ].s		= X * 32;
			_tileInfo[ tileIdx ].t		= Y * 32;
			
		//	printf("%i,%i,%i\n", X*32, Y*32, textureIdx );
			
			int tileX, tileY;
			for( tileX = 0; tileX < 4; tileX++ ) {
				for( tileY = 0; tileY < 4; tileY++ ) {
					u_int16_t tilePieceInfo = tile.micro_tiles[(3-tileY) * 4 + tileX];
					
					int		tilePieceIndex = ( tilePieceInfo / 2 );
					BOOL	flipTile = ( tilePieceInfo % 2 == 0 );

					int subTileX;
					int subTileY;
					int XToDraw;
					int YToDraw;
					
					unsigned int * color = malloc( sizeof( unsigned int ) * 4 );
					
					tile_piece_t piece = [_pieceList pieceAtIndex: tilePieceIndex ];

					for( subTileX = 0; subTileX < 8; subTileX++ ){
						for( subTileY = 0; subTileY < 8; subTileY++ ) {
							
							[_palette color:color forPixelAtIndex:piece.pixels[subTileY][7-subTileX]];
							
							int cTileX = tileX;
							int cTileY = tileY;
							int cSubTileX = subTileX;
							int cSubTileY = subTileY;
							
							cTileY = 3 - cTileY;
							if( flipTile ) {
								cSubTileX = 7 - cSubTileX;
							}
							
							XToDraw = (cTileX*8+(cSubTileX));							
							YToDraw = (cTileY*8+(cSubTileY));
							
							int X_CORD = X * 32 + XToDraw;
							int Y_CORD = Y * 32 + YToDraw;
																					
							pixels[ 3 * ( Y_CORD * TEXTURE_WIDTH + X_CORD )] = color[ 0 ];
							pixels[ 3 * ( Y_CORD * TEXTURE_WIDTH + X_CORD ) + 1] = color[ 1 ];
							pixels[ 3 * ( Y_CORD * TEXTURE_WIDTH + X_CORD ) + 2] = color[ 2 ];
						}
					}
				}
			}
			
			X++;
			if( X >= 32 ) {
				X = 0;
				Y++;
			}
			
			if( Y >= 32 ) {
				Y = 0;
			}
		}
		
		/* Copy to GL space and free the storage */
		glTexImage2D( GL_TEXTURE_RECTANGLE_ARB , 0 , GL_RGB5, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0 , GL_RGB, GL_UNSIGNED_BYTE , pixels );
		free( pixels );
	}
}

- (float)textureS:(int)tileId {
	return _tileInfo[ tileId ].s;
}

- (float)textureT:(int)tileId {
	return _tileInfo[ tileId ].t;
}

- (GLuint)textureID:(int)tileId {
	return _tileInfo[ tileId ].texID;
}

- (void)invalidatePiece:(int)index {
	[_tilesBitmap replaceObjectAtIndex:index withObject:ARRAY_PLACEHOLDER];
}

- (NSColor *)minimapColorForTileAtIndex:(int)index {
	tile_t tile = [self tileAtIndex:index];
	u_int16_t tilePieceInfo = tile.micro_tiles[0];
	
	int		tilePieceIndex = ( tilePieceInfo / 2 );
	
	tile_piece_t piece = [_pieceList pieceAtIndex: tilePieceIndex ];
	NSColor * c = [_palette colorAtIndex:piece.pixels[0][0]];
	return c;
}

static double total_loading_seconds = 0.0;
- (NSImage *)bitmapForTileAtIndex:(int)index {
	if( [_tilesBitmap objectAtIndex:index] == ARRAY_PLACEHOLDER ) {
		NSDate * then = [NSDate date];
		NSImage * image = [[NSImage alloc] initWithSize:NSMakeSize(32,32)];
		[image lockFocus];
		NSBitmapImageRep * imageRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
		[image unlockFocus];
		[image release];
		int tileX, tileY;
		
		tile_t tile = [self tileAtIndex:index];

		for( tileX = 0; tileX < 4; tileX++ ) {
			for( tileY = 0; tileY < 4; tileY++ ) {
				u_int16_t tilePieceInfo = tile.micro_tiles[(3-tileY) * 4 + tileX];
				
				int		tilePieceIndex = ( tilePieceInfo / 2 );
				BOOL	flipTile = ( tilePieceInfo % 2 == 0 );

				int subTileX;
				int subTileY;
				int XToDraw;
				int YToDraw;
				
				tile_piece_t piece = [_pieceList pieceAtIndex: tilePieceIndex ];

				for( subTileX = 0; subTileX < 8; subTileX++ ){
					for( subTileY = 0; subTileY < 8; subTileY++ ) {
						
						NSColor * c = [_palette colorAtIndex:piece.pixels[subTileY][7-subTileX]];
						
						int cTileX = tileX;
						int cTileY = tileY;
						int cSubTileX = subTileX;
						int cSubTileY = subTileY;
						
						cTileY = 3 - cTileY;
						
						if( flipTile ) {
							cSubTileX = 7 - cSubTileX;
						}
						
						XToDraw = (cTileX*8+(cSubTileX));							
						YToDraw = (cTileY*8+(cSubTileY));
						[imageRep setColor:c atX:XToDraw y:YToDraw];
					}
				}
			}
		}
		
		NSImage * resultImage = [[NSImage alloc] initWithSize:NSMakeSize( [imageRep pixelsWide], [imageRep pixelsHigh] )];
		[resultImage addRepresentation:imageRep];
		[_tilesBitmap replaceObjectAtIndex:index withObject:resultImage];
		[resultImage release];
		NSDate * now = [NSDate date];
		total_loading_seconds += [now timeIntervalSinceDate: then ];
	//	printf("%f loading seconds\n", total_loading_seconds );
	}
	
	return [_tilesBitmap objectAtIndex:index];
}

- (void)dealloc {
	free( _tiles );
	[_tilesBitmap release];
	[_palette release];
	[_pieceList release];
	[_colorIDList release];
	[super dealloc];
}

- (int)textureAtIndex:(int)index {
	return _textureID[ index ];
}

- (int)textureCount {
	return _textureCount;
}
@end